package com.topsun.posclient.common.dao;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Repository;

import com.topsun.posclient.common.bean.PageBean;
import com.topsun.posclient.common.bean.QueryConditions;
import com.topsun.posclient.common.util.ProjectUtil;

@Repository("baseDao")
public class BaseDao {
	/**
     * 以私有成员变量保存SessionFactory
     */
	@Autowired
	@Resource(name = "sessionFactory")
    private SessionFactory sessionFactory;
	
    /**
     * 以私有成员变量保存Hibernate模板HibernateTemplate
     */
    private HibernateTemplate hibernateTemplate;

    /**
     * 设置注入SessionFactory必须的setter方法(系统自动调用)
     *
     * @param sessionFactory 会话工厂，配置数据源及事务
     */
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /**
     * 获取Hibernate模板对象
     *
     * @return HibernateTemplate  Hibernate模板对象
     */
    public HibernateTemplate getHibernateTemplate() {
        //首先检查原来的HibernateTemplate实例是否存在
        if (this.hibernateTemplate == null) {
            //如果不存在，则创建一个HibernateTemplate实例
            this.hibernateTemplate = new HibernateTemplate(this.sessionFactory);
        }
        return this.hibernateTemplate;
    }

    //=================================以下代码为Hibernate实现的常用方法===========================//

    /**
     * 根据主键加载特定持久化类的实例。
     *
     * @param entityClass 持久化类
     * @param id          主键
     * @return Object 取得的实例
     */

	@SuppressWarnings("unchecked")
	public Object get(Class entityClass, Serializable id) {
        return getHibernateTemplate().get(entityClass, id);
    }

    /**
     * 根据主键加载特定持久化类的实例。
     * <p/>
     * load与get区别：<br><br>
     * 1)load只会在hibernate的内部缓存和二级缓存中的查找实例，而get方法仅在内部缓存中查找，<br>
     * 如果没有发现对应的数据，将越过二级缓存，直接调用SQL完成数据读取。<br>
     * <p/>
     * 2)如果对象不存在，get返回null，而load方法抛出异常，因此在使用load方法时，要确认查询的主键id一定是存在的。
     *
     * @param entityClass 持久化类
     * @param id          主键
     * @return Object 取得的实例
     * @throws org.springframework.dao.DataAccessException
     *          数据访问异常，是一个运行时异常
     */
	@SuppressWarnings("unchecked")
	public Object load(Class entityClass, Serializable id) throws DataAccessException {
        return getHibernateTemplate().load(entityClass, id);
    }

    /**
     * 保存新的实例至持久层。
     *
     * @param entity 要保存的新实例
     * @throws DataAccessException 数据访问异常，是一个运行时异常
     */
    public void save(Object entity) throws DataAccessException {
    	
        getHibernateTemplate().save(entity);
    }

    /**
     * 根据实例状态，选择保存或者更新实例至持久层。
     *
     * @param entity 要保存的实例
     * @throws DataAccessException 数据访问异常，是一个运行时异常
     */
    public void saveOrUpdate(Object entity) throws DataAccessException {
        getHibernateTemplate().saveOrUpdate(entity);
    }

    /**
     * 更新现有实例至持久层。
     *
     * @param entity 要更新的实例
     * @throws DataAccessException 数据访问异常，是一个运行时异常
     */
    public void update(Object entity) throws DataAccessException {
        getHibernateTemplate().update(entity);
    }

    /**
     * 删除现有实例，同步持久层。
     *
     * @param entity 要删除的实例
     * @throws DataAccessException 数据访问异常，是一个运行时异常
     */
    public void delete(Object entity) throws DataAccessException {
        getHibernateTemplate().delete(entity);
    }

    /**
     * 根据主键删除现有持久化类的实例，同步持久层。
     *
     * @param entityClass 持久化类
     * @param id          主键
     * @throws DataAccessException 数据访问异常，是一个运行时异常
     */
    public void delete(Class entityClass, Serializable id) throws DataAccessException {
        getHibernateTemplate().delete(this.get(entityClass, id));
    }

    /**
     * 删除集合内全部持久化类实例，同步持久层。
     *
     * @param entities 要删除的实例集合
     * @throws DataAccessException 数据访问异常，是一个运行时异常
     */
    public void deleteAll(Collection entities) throws DataAccessException {
        getHibernateTemplate().deleteAll(entities);
    }

    /**
     * 根据条件使用HQL语句查询数据。<br>
     * <p/>
     * 具有功能：<br>
     * 1）支持查询分页，该方法会利用数据库本身的分页技术实现。说明如下：<br>
     * &nbsp;&nbsp;&nbsp;&nbsp;a)如果数据库(如MySQL,Oracle,SQL Server2005等)支持limit n,m，查询效率最高；<br>
     * &nbsp;&nbsp;&nbsp;&nbsp;b)如果数据库(如informix,Sybase 12.5.3,SQL Server等)支持top n，查询效率次之（查询结果越大，效率越低）； <br>
     * &nbsp;&nbsp;&nbsp;&nbsp;c)如果以上两种均不支持，查询效率最低。<br>
     * 2）支持查询总记录数<br>
     * 3）支持order by，group by,having等 <br>
     * <p/>
     * 不支持功能：<br>
     * 1）不支持select里的嵌套子查询。如不允许这种用法：select a,b,(select c from table1) as d from table2 ...<br>
     * 2）条件与条件之间不支持or，而是用and。<br>
     * <p/>
     * 示例如下：<br>
     * <pre>
     * 1)查询所有用户信息:
     * <p/>
     * xxxDao.select("from TUser",null,null,null,0,0);
     * <p/>
     * 2)查询用户名含有"admin"开头，注册日期2006-12-01之前前10条用户信息(用户名及注册日期):
     * <p/>
     * xxxDao.select("select userName,createDate from TUser",
     *               new String[]{"userName","createDate"},
     *               new String[]{"like",">="},
     *               new Object[]{"admin%","2006-12-01"},0,10);
     * <p/>
     * <b>注意：对于多对象关联查询，必须指定返回的对象类型。</b><br>
     * 示例如下：
     * <pre>
     * <b>1. 插寻结果bean，A的属性a、b来源于已影射的bean M,N</b>
     * class A{
     *      String a,b;
     *      public A(String a,String b){
     *          this.a=a;
     *          thia.b=b;
     *      }
     * }
     * <p/>
     * <b>2.编写查询方法</b>
     * Query q= session.createQuery("select new A(M.a,N.b) from M as M,N as N where M.id=N.id");
     *
     * @param hql           HQL查询语句（不带Where条件）。不允许在select段内使用子查询，如不允许这种用法：
     *                      select a,b,(select c from table1) as d from table2 ...
     *                      1)对于查询全部对象属性，(<b>select *</b>)不可写。如：from TUser；
     *                      2)对于查询部分对象属性，则必须写完整，如：select userName,password from TUser;
     * @param propertyNames 查询条件的属性名列表
     * @param operators     查询条件的操作符列表，如果查询条件中存在不为<b>=</b>的操作符，需要填写该列表，否则为null，
     *                      应与属性名列表一一对应。操作符包括=, >=, <=, <>, !=, like。
     * @param values        查询条件的值列表，该列表应当与属性列表一一对应
     * @param offset        查询结果的起始行，从0开始。如果不需要，则设置为-1。
     * @param size          查询结果的最大行数。如果不需要，则设置为-1
     * @param isTotalSize   是否需要返回本次查询的总记录数，默认false
     * @param orderBy       排序字符串,不含order by字符串，如orderBy="a desc,b asc",最后生成为：order by a desc,b asc
     * @param groupBy       分组字符串,不含group by 字符串，如groupBy="a desc,b asc",最后生成为：group by a desc,b asc
     * @param otherCause    where后面的其它语句，如排序(order by),分组(group by)及聚集(having)。
     *                      如"group by name order by name desc"
     * @return Object[]    有两个值，第一个值表示查询结果列表List list = (List)Object[0]
     *         第二个表示查询返回的总记录数，int count = ((Integer)Object[1]).intValue;
     * @throws com.longtop.intelliweb.sample.exception.Exception
     *          基础不可控异常类
     *          </pre>
     */
    @SuppressWarnings("unchecked")
	public Object[] query(final String hql, final String[] propertyNames, final String[] operators,
                          final Object[] values, final int offset, final int size, final boolean isTotalSize,
                          final String orderBy, final String groupBy, final String otherCause)
            throws Exception {

        Map map = (Map) this.getHibernateTemplate().execute(
                new HibernateCallback() {
					public Object doInHibernate(Session session) throws HibernateException, SQLException {
                        Query query;
                        String countSql;
                        String fullSql;
                        Integer count = new Integer(0);
                        Map map = new HashMap();
                        String where = "";

                        if (hql == null || hql.trim().equals("")) {
                            //log.logError(Message.getMessage(ResourceConstants.E_BASE_0000));
								throw new SQLException();
                        }

                        if (propertyNames != null && propertyNames.length > 0 && values != null && values.length > 0) {
                            if (propertyNames.length != values.length) {
                                // log.logError(Message.getMessage(ResourceConstants.E_BASE_0000));
                                throw new HibernateException("propertyNames length noe equal values length");
                            }

                            if (operators != null && propertyNames.length != operators.length) {
                                //log.logError(Message.getMessage(ResourceConstants.E_BASE_0000));
                                throw new HibernateException("propertyNames length noe equal operators length");
                            }

                            for (int i = 0; i <= propertyNames.length - 1; i++) {
                                if ("".equals(where)) {
                                    where = " where ";
                                } else {
                                    where += "and ";
                                }
                                if (operators != null && operators[i].equalsIgnoreCase("isnull")) {
                                    where += propertyNames[i] + " is null ";
                                } else if (operators != null && operators[i].equalsIgnoreCase("isnotnull")) {
                                    where += propertyNames[i] + " is not null ";
                                } else if (operators != null && operators[i].equalsIgnoreCase("isempty")) {
                                    where += propertyNames[i] + " = '' ";
                                } else if (operators != null && operators[i].equalsIgnoreCase("isnotempty")) {
                                    where += propertyNames[i] + " <> '' ";
                                } else {
                                    where += propertyNames[i] + (operators == null || operators[i] == null ? "=" : " " + operators[i]) + " ? ";
                                }
                            }

                            fullSql = hql + where;
                            fullSql += orderBy == null || orderBy.trim().equals("") ? "" : " order by " + orderBy;
                            fullSql += groupBy == null || groupBy.trim().equals("") ? "" : " group by " + groupBy;
                            fullSql += otherCause == null || otherCause.trim().equals("") ? "" : " " + otherCause;

                            query = session.createQuery(fullSql);

                            for (int i = 0; i <= values.length - 1; i++) {
                                if (operators != null && operators[i].equalsIgnoreCase("isnull")) continue;
                                if (operators != null && operators[i].equalsIgnoreCase("isnotnull")) continue;
                                if (operators != null && operators[i].equalsIgnoreCase("isempty")) continue;
                                if (operators != null && operators[i].equalsIgnoreCase("isnotempty")) continue;
                                query.setParameter(i, values[i]);
                            }

                        } else {

                            fullSql = hql + where;
                            fullSql += orderBy == null || orderBy.trim().equals("") ? "" : " order by " + orderBy;
                            fullSql += groupBy == null || groupBy.trim().equals("") ? "" : " group by " + groupBy;
                            fullSql += otherCause == null || otherCause.trim().equals("") ? "" : " " + otherCause;

                            query = session.createQuery(fullSql);
                            
                        }

                        //如果需要统计本次查询总记录数
                        if (isTotalSize) {

                            //生成统计总数查询语句（不能累加order by ，否则效率会受影响）
                            countSql = hql + where;
                            countSql += groupBy == null || groupBy.trim().equals("") ? "" : " group by " + groupBy;
                            countSql += otherCause == null || otherCause.trim().equals("") ? "" : " " + otherCause;

                            //生成查询总记录的hql语句。在hql中，不允许在select段内使用子查询，如不允许这种用法：
                            // select a,b,(select c from table1) as d from table2 ...
                            countSql = "select count(*) from " + countSql.substring(countSql.toLowerCase().indexOf("from") + 5);
                            Query query2 = session.createQuery(countSql);

                            if (values != null) {
                                for (int i = 0; i <= values.length - 1; i++) {
                                    if (operators != null && operators[i].equalsIgnoreCase("isnull")) continue;
                                    if (operators != null && operators[i].equalsIgnoreCase("isnotnull")) continue;
                                    if (operators != null && operators[i].equalsIgnoreCase("isempty")) continue;
                                    if (operators != null && operators[i].equalsIgnoreCase("isnotempty")) continue;
                                    query2.setParameter(i, values[i]);
                                }
                            }
                            count = new Integer(String.valueOf(query2.list().get(0)));
                        }

                        if (offset > 0) {
                            query.setFirstResult(offset);
                        }

                        if (size > 0) {
                            query.setMaxResults(size);
                        }

                        map.put("list", query.list());
                        map.put("count", count);
                        return map;
                    }
                }
        );

        return new Object[]{map.get("list"), map.get("count")};
    }
	
    /**
     * 根据条件使用HQL语句查询数据，返回符合条件指定范围的数据，HQL语句不带where 条件，仅为select ... from ...部分）。<br>
     *
     * @see com.longtop.intelliweb.sample.base.dao.IBaseDAO#query(String,String[],String[],Object[],int,int,boolean,String,String,String)
     */
    public Object[] queryPageByCondition(final String hql, final String[] propertyNames, final String[] operators,
                                         final Object[] values, final int offset, final int size,
                                         final boolean isTotalSize) throws Exception {
        return this.query(hql, propertyNames, operators, values, offset, size, isTotalSize, null, null, null);
    }

    /**
     * 根据条件使用HQL语句查询数据，返回符合条件指定范围的list，HQL语句不带where 条件，仅为select ... from ...部分）。<br>
     *
     * @see com.longtop.intelliweb.sample.base.dao.IBaseDAO#query(String,String[],String[],Object[],int,int,boolean,String,String,String)
     */
    public List queryPageForListByCondition(final String hql, final String[] propertyNames, final String[] operators,
                                            final Object[] values, final int offset, final int size) throws Exception {
        Object[] result = this.query(hql, propertyNames, operators, values, offset, size, false, null, null, null);
        return result[0] != null ? (List) result[0] : null;
    }

    /**
     * 根据HQL语句查询，返回符合条件内指定范围的数据。HQL可为完整语句。<br>
     * 如select ... from ... where ... order by ... group by ...。<br>
     *
     * @see com.longtop.intelliweb.sample.base.dao.IBaseDAO#query(String,String[],String[],Object[],int,int,boolean,String,String,String)
     */
    public Object[] queryPage(final String hql, final int offset, final int size, final boolean isTotalSize)
            throws Exception {
        return this.query(hql, null, null, null, offset, size, isTotalSize, null, null, null);
    }

    /**
     * 根据HQL语句查询数据，返回符合条件指定范围的list。HQL可为完整语句。<br>
     * 如select ... from ... where ... order by ... group by ...。<br>
     *
     * @see com.longtop.intelliweb.sample.base.dao.IBaseDAO#query(String,String[],String[],Object[],int,int,boolean,String,String,String)
     */
    public List queryPageForList(final String hql, final int offset, final int size) throws Exception {
        Object[] result = this.query(hql, null, null, null, offset, size, false, null, null, null);
        return result[0] != null ? (List) result[0] : null;
    }

    /**
     * 根据条件使用HQL语句查询，返回所有符合条件的list。可用list.size()得到返回的总记录数。<br>
     * HQL语句不带where 条件，仅为select ... from ...部分。<br>
     *
     * @see com.longtop.intelliweb.sample.base.dao.IBaseDAO#query(String,String[],String[],Object[],int,int,boolean,String,String,String)
     */
    public List queryAllByCondition(final String hql, final String[] propertyNames, final String[] operators,
                                    final Object[] values) throws Exception {
        Object[] result = this.query(hql, propertyNames, operators, values, -1, -1, false, null, null, null);
        return result[0] != null ? (List) result[0] : null;
    }

    /**
     * 根据HQL语句查询，返回所有符合条件的list(可用list.size()得到返回的总记录数)。<br>
     * HQL可为完整语句。如select ... from ... where ... order by ... group by ...。<br>
     *
     * @see com.longtop.intelliweb.sample.base.dao.IBaseDAO#query(String,String[],String[],Object[],int,int,boolean,String,String,String)
     */
    public List queryAll(final String hql) throws Exception {
        Object[] result = this.query(hql, null, null, null, -1, -1, false, null, null, null);
        return result[0] != null ? (List) result[0] : null;
    }


    /**
     * 根据hsql批量更新(删除／修改)。
     * <p/>
     * 示例如下：
     * <pre>
     * 1)批量删除
     * String hql = "delete Student s where s.sage>25";
     * int intCount = this.batchUpdate(hql);
     * 2)批量更新
     * String hql = "update Student s set s.sage='22' where s.id=11"
     *  int intCount = this.batchUpdate(hql);
     *
     * @param hql HQL更新及删除语句
     * @return 更新或删除成功的记录数
     */
    public int batchUpdate(String hql) {
        int result = 0;
        Session session = getHibernateTemplate().getSessionFactory().openSession();
        Transaction trans = null;
        try {
            trans = session.beginTransaction();
            result = session.createQuery(hql).executeUpdate();
            trans.commit();
            trans = null;
        } catch (HibernateException e) {
            if (trans != null)
                trans.rollback();
            e.printStackTrace();
        } finally {
            session.close();
        }

        return result;
    }

	/* 分页查询符合条件的数据,生成PageBean返回
	 * @see com.tango.meiniu.base.dao.IBaseDao#queryPageByCondition(java.lang.String, java.lang.String[], java.lang.String[], java.lang.Object[], com.tango.meiniu.base.util.PageBean)
	 */
	public PageBean queryPageByCondition(String hql, String[] propertyNames,
			String[] operators, Object[] values, PageBean pageBean)
			throws Exception {
		pageBean.setTotalProperty(this.queryAllByCondition(hql, propertyNames, operators, values).size()); // 设置总记录数
		// 以下是根据总记录数和每页大小计算总页数
		if (pageBean.getTotalProperty() % pageBean.getLimit() == 0) {
			pageBean.setTotalPage(pageBean.getTotalProperty()/ pageBean.getLimit());
		} else {
			pageBean.setTotalPage(pageBean.getTotalProperty()/ pageBean.getLimit() + 1);
		}
		// 如果当前页号少于1的话，当前页号等于1
		if (pageBean.getCurrentPage() < 1) {
			pageBean.setCurrentPage(1);
		}
		// 如果当前页号大于总页数的话，当前页号等于总页数
		if (pageBean.getCurrentPage() > pageBean.getTotalPage()) {
			pageBean.setCurrentPage(pageBean.getTotalPage());
		}
		int offset = Integer.parseInt(pageBean.getStart()+"");
		int size = Integer.parseInt(pageBean.getLimit()+"");
		
        List list = this.queryPageForListByCondition(hql, propertyNames, operators, values, offset, size);
		pageBean.setRoot(list);
		return pageBean;
	}
	
	/**
	 * @param hql
	 * @param condition
	 * @return
	 * @throws Exception 
	 */
	public List find(String hql, QueryConditions condition) throws Exception{
		 
		int index = condition.getPropertyNames().size();
		String[] propertyNames = new String[index];
		String[] operators = new String[index]; 
		
		for (int i=0; i< index; i++) {
			propertyNames[i] = (String) condition.getPropertyNames().get(i);   
			operators[i] = (String) condition.getOperators().get(i); 
		}
 
		Object[] objs = query(
				hql, 
				propertyNames,
				operators,
				condition.getValues().toArray(), 
				0,
				1000,
				false,
				condition.getOrderBy(),
				condition.getGroupBy(),
				condition.getOtherHql()
				); 
		List result = (ArrayList) objs[0];
		return result;  
		
	}	
	
	@SuppressWarnings("unchecked")
	public PageBean queryPageByCondition(String hql,
			QueryConditions conditions, PageBean pageBean)
			throws Exception {
    	String[] propertyNames = null;
    	String[] operators = null;
    	Object[] values = null;
    	
    	if(conditions != null){
    	    propertyNames = ProjectUtil.listToStrArray(conditions.getPropertyNames());
            operators = ProjectUtil.listToStrArray(conditions.getOperators());
			List valuesList = conditions.getValues();
            values = valuesList == null || valuesList.size() == 0 ? null : valuesList.toArray();
    	}
        //执行查询
		return this.queryPageByCondition(hql, propertyNames, operators, values, pageBean);
	}
	
}